<?php
/*
 * Templates engine for Gepard based on regular expessions
 *
 * (c) Taras Kudla, 2009
 */
class engine_pcre extends kernel implements tplengine{
    	
    	
    	private $GlobalData = array();
    	private $LanguageLink = null;
    	public $CurrentTplMod = null;
    	
    	# binding var 
    	public function bindvar($varname,$varvalue){
    	
    	}
    	
    	public function deletevar($varname){
    	
    	}
    	
    	public function clearallglobal(){
    	
    	}
    	public function setvars ( $vars ) {
    		$this->GlobalData = &$vars;
    	}    	
    	
	# simple parser mechanism, with data array, and link to language parsed object ( lang_image )
	public function & parsetext ( & $text,$data = null, $language = null ){
		if( !is_array($this->GlobalData) || count($this->GlobalData)>0){
			$this->core->error("VIEW","GlobalData should be array, and empty");
			return false;
		}
		if( !is_array($data))$data=array();	
			$this->GlobalData = &$data;
		if( is_object($language) && get_class($language)=="lang_image" ){
			$this->LanguageLink=$language;
		}
		$text = $this->parser($text);
		$this->LanguageLink = null;
		$this->GlobalData = array();
		return $text;
	}

	# main parsing method
	public function & parser (& $text){
	# LOOP
	/*
	 * FIXME: error in pattern, system crash: mod/pages/tpl/pageslist.html in php5.2.8> win32 TS
	 */
	//$looppattern = "/\{ *\%loop +([^{]+) *\}(((?!{ *\%loop +).)*?)\{ *\/loop *\}/is";
	$looppattern = '/\{ *\%loop +(([A-z]{1}) *= *)?([0-9]{1,3}|[0-9]{1,3}\.\.[0-9]{1,3}||\#?[A-z0-9_\]\[]+) *\}(((?!{ *loop +).)*?)\{ *\/loop *\}/is';
	//$looppattern = "/\{ *\%loop +(.+) *\}(((?!{ *\%loop +).)*?)\{ *\/loop *\}/Uis";	
	while(preg_match($looppattern,$text)){
		$text=preg_replace_callback($looppattern,array(&$this,"r_loop"),$text);
	}
	# VAR
	$text  =  preg_replace_callback("/\{ *\#([A-z0-9_\]\[]+) *\}/i",array(&$this,"r_var"),$text);
	# in_array implementation
	$text  =  preg_replace_callback("/\{ *\%in_array *\( *([A-z0-9_-]+) *, *#([A-z0-9_\]\[]+) *\) *\}/i",array(&$this,"r_in_array"),$text);	
	# SYS
	$text  =  preg_replace_callback("/\{ *\%sys\s+(version|runtime|kernel|time|reqfiles|memusage|htmlheaders|title|phpversion|language|debug|calltrace|encoding|userid|rand|platform) *\}/i",array(&$this,"r_sys"),$text);		
	# TYPEOF
	$text  =  preg_replace_callback("/\{ *\%typeof\s+\#([A-z0-9_\]\[]+) *\}/i",array(&$this,"r_typeof"),$text);
	# COUNT
	$text  =  preg_replace_callback("/\{ *\%count\s+\#([A-z0-9_\]\[]+) *\}/i",array(&$this,"r_count"),$text);	
	# IF
	$ifpattern = "/\{ *\%if *([\"'])?( *|[A-z0-9-_]+)[\"']? +(=|>|<|not|%|<=|>=|eq) *([\"'])?([A-z0-9-_]+| *)[\"']? *\}(((?!{ *\%if +).)*?)\{ *\/if *\}/si";
	while(preg_match($ifpattern,$text)){
		$text  =  preg_replace_callback($ifpattern,array(&$this,"r_if"),$text);
	}
	# ACTION CALL
	$text  =  preg_replace_callback("/\{ *%callaction +([A-z0-9_-]+) *\}/i",array(&$this,"r_extcall"),$text);
	# LANG
	$text  =  preg_replace_callback("/\{ *%lang +([A-z0-9_-]+) *\}/i",array(&$this,"r_lang"),$text);
	# SYSLANG
	$text  =  preg_replace_callback("/\{ *%syslang +([A-z0-9_-]+) *\}/i",array(&$this,"r_syslang"),$text);
	# ACL
	$text  = preg_replace_callback("/\{ *\%acl *\( *([A-z0-9-_]+)(,[A-z0-9-?&=]+)?(,[^),}]+)?(,[A-z0-9-?&=\"\']+)?(,[A-z0-9-_\/]+)? *\)? *\) *\}/i",array(&$this,"r_acl"),$text);
	# ACTION LINK
	$text  = preg_replace_callback("/\{ *\%action *\( *([A-z0-9-_]+)(,[A-z0-9-?&=]+)?(,[A-z0-9-_\/]+)? *\)? *\}/i",array(&$this,"r_action"),$text);	
	# DATE
	$text  = preg_replace_callback("/\{ *\%date *\( *([A-z0-9 -:%,.]+)? *\) *\}/i",array(&$this,"r_date"),$text);
	return $text;
	}
	
	
	/*
 	 * Replacements
 	 */

	# if operator simple implementation
	public function r_if($m) {
		$expression = $m[3];
		$m[2]=trim($m[2]);
		$m[2]=strlen($m[2])==0?null:$m[2];
		$m[5]=trim($m[5]);
		$m[5]=strlen($m[5])==0?null:$m[5];
        $true = false;
        $else = null;
        if(preg_match("/(.+)\{else\}(.+)/is",$m[6],$tmp)){
                $else = $tmp[2];
		$m[6] = $tmp[1];
		}
		# preparing for defines
		if( $m[5]=="true" || $m[5]=="false"){
			$m[5]=($m[5]=="true")?"1":"0";
		}
		if( $m[5]=="null"){
			$m[5]= null;
		}
		if( $m[2]=="true" || $m[2]=="false"){
			$m[2]=($m[2]=="true")?"1":"0";
		}	
		if( $m[2]=="null"){
			$m[2]= null;
		}	
        switch ($m[3]){
	    case "eq":
            case "=" :     
            	$true=($m[2] == $m[5])?true:false;
            	//$true=( $m[5]=="true" || $m[5]=="false")?($m[2]==true?true:false):($m[2] == $m[5])?true:false;
                break;
            case ">":
                $true=($m[2] > $m[5]) ? true:false;
                break;            case ">=":
                $true=($m[2] >= $m[5]) ? true:false;
                break;
            case "<=":
	      $true=($m[2] <= $m[5]) ? true:false;
                break;
            case "<":
                $true=($m[2] < $m[5]) ? true:false;
                break;
            case "not":
                $true=($m[2] != $m[5]) ? true:false;
                break;
	    case "%":
                $true=((int) $m[2] % (int) $m[5] && ((int) $m[5] != 0)) ? true : false;
                break;
            default:
                $true=false;
        }
        return ($true)?$m[6]:($else!=null?$else:null);
	}
	# simple loops implementation
	public function & r_loop($data){
		$iter = (preg_match("/^([A-z]{1})$/",$data[2],$trash)?$trash[1]:"i");
		$t=null;
		//var_dump($data);
		if( preg_match("/^([0-9]{1,3})\.\.([0-9]{1,3})$/",$data[3],$tr)){
			$i= (int)$tr[2];
			$start=(int)$tr[1];
		}else {	
			//print "var: ".$data[3];
			$i = (preg_match("/^#([A-z0-9_\[\]]+)$/",$data[3],$tmp)?$this->r_var(array(null,$tmp[1]),"count"):(int)$data[3]);
			$start=0;
		}
		if( $i==0)
			return $t;
		//print " loop: ".$start."-".$i.", iter:".$iter."<br>";
		for($k=$start;$k<$i;$k++){ 
            $p = preg_replace("/\[".$iter."\]/", "[".$k."]", $data[4]);
            $p = preg_replace("/\{".$iter."\}/si", ($k+1), $p);
            $t.=$p;
		}
		return $t;
	}
	
	# variables replacing
	public function r_var($data,$operation="default"){
	    $var = $data[1];
	    if (is_array($this->GlobalData)){
            $p1 = preg_replace("/\[([^0-9]+|[A-z_]+[0-9]{1,3})\]/", "[\"$1\"]", $var);
            $p = preg_replace("/^([^\[]+)/", "[\"$1\"]", $p1);

	    $result = null;
	    /*
	     * var detection
	     */
	    preg_match_all("/\[\"?([A-z0-9_]+)\"?\]/",$p,$tmp);
	    $vars = $tmp[1];
	    $deep = count($vars);
	    $varname = $vars[0];
	    # array calls is to deep, error
	    if( $deep > 4 ){
	    	return "Array ( {$data[1]} ) deep is to large";
	    }
	    if( $deep>0){
	    	if( array_key_exists($vars[0],$this->GlobalData))
	    	$var = $this->GlobalData[$vars[0]];
	    	else return "Unknown part  '{$vars[0]}' of variable #$varname in template engine";
	    }
		if( $deep>1){
	    	if( array_key_exists($vars[1],$var))
	    	$var = $var[$vars[1]];
	    	else return "Unknown part  '{$vars[1]}' of variable #".$varname." in template engine";
	    }
		if( $deep>2){
	    	if( array_key_exists($vars[2],$var))
	    	$var = $var[$vars[2]];
	    	else return "Unknown part  '{$vars[2]}' of variable #".$varname."[".$vars[1]."] in template engine";
	    }	    
		if( $deep>3){
	    	if( array_key_exists($vars[3],$var))
	    	$var = $var[$vars[3]];
	    	else return "Unknown part  '{$vars[3]}' of variable #".$varname."[".$vars[1]."]"."[".$vars[2]."] in template engine";
		}
	    switch($operation){
	    	case "count" : $result = (is_array($var)?count($var):((!is_object($var) && !is_resource($var)) ?strlen((string)$var):0)); break;  
	    	case "typeof": $result = is_array($var)?'array':(is_object($var)?'object':(is_numeric($var)?'numeric':(is_string($var)?'string':'undefined')));break;
	    	default: if(is_bool($var))$result=(int)$var; else $result=&$var;
	    }
	    return $result;	
       }
	}

	# in_array implementation
	public function r_in_array($data){
		$type=$this->r_var(array(null,$data[2]),"typeof");
		return in_array($data[1],$this->r_var(array(null,$data[2])))?1:0;
	}

	# typeof impelmentation
	public function r_typeof($data){
		return $this->r_var(array(null,$data[1]),"typeof");
	}
	# count array length or number of characters in string
	public function r_count($data){
		return $this->r_var(array(null,$data[1]),"count");
	}
	# some usfull replacements of system information 
	public function r_sys($data){
		switch($data[1]){
			case "kernel": return $this->core->GetKernel(); break;	
			case "version": return $this->core->GetVersion(); break;		
			case "htmlheaders" : return $this->core->base->GetHTMLHeaders(); break;	
			case "language": return $this->core->get("language"); break;		
			case "debug": return $this->core->GetDebug(_STRING); break;		
			case "runtime": return $this->core->runtimepoint(); break;
			case "time"    : return date("U"); break;
			case "memusage": return $this->core->GetMemoryUsage(); break;
			case "reqfiles": return count(get_required_files()); break;
			case "userid"  : return $this->core->user->GetUserID(); break;
			case "platform" :return PHP_OS; break;
			case "title" :return $this->core->base->gettitle(); break;
			case "phpversion" : return PHP_VERSION; break;
			case "calltrace": return "calltrace"; break;
			case "rand"	: return rand(0,100); break;
			case "encoding": return $this->core->get("encoding"); break;		
		}
	}
	# language files information replacing
	public function r_lang($data){
		if( is_object($this->LanguageLink) && get_class($this->LanguageLink)=="lang_image"){
			return $this->LanguageLink->get($data[1]);
		}	
		if ($this->core->Getenvlayer() == _ENV_KERNEL){
			return $this->core->lang->get($data[1]);
		}
		return $this->core->lang->getmodlangbykey($this->CurrentTplMod,$data[1]);
	}
	# syslang replacing
	public function r_syslang($data){
		return $this->core->lang->get($data[1]);
	}

	# external calls, note: it checks permission to access
	public function & r_extcall($data){
		$data = $this->core->base->callaction($data[1]);
		if( !is_string($data)) return null;
		return $data;
	}

	# action links in <a > </a> tag (acl), it's ugly, and i don't reccomend to use it
	public function r_acl($data){
		$action = $data[1];
		$params = (isset($data[2]) && strcmp($data[2],",null")!=0 )?preg_replace("/,/","",$data[2]):null;
		$value = (isset($data[3]) && strcmp($data[3],",null")!=0)?preg_replace("/,/","",$data[3]):null;
		$optionalhtml = (isset($data[4]) && strcmp($data[4],",null")!=0)?preg_replace("/,/","",$data[4]):null;
		$runlevelname = (isset($data[5]) && strcmp($data[5],",null")!=0 )?preg_replace("/,/","",$data[5]):null;
		$env = $this->core->Getenvcaller();
		if(preg_match("/^([A-z0-9_]+)-([A-z0-9]+)$/i",$action,$tmp)){
			$module = $tmp[1];
			$function = $tmp[2];	
		} else {
			$module = $env["MOD"];
		$function  = $action;
		}
		return $this->core->base->GetActionLink( $function,$params,$value,$optionalhtml,$runlevelname,$function);	
	}
	
	# action links ( to another action in gepard )
	public function r_action($data){
		$action = (strcmp($data[1],"null")!=0 )?$data[1]:null;
		$module = null;
		$function = null;
		$params = (isset($data[2]) && strcmp($data[2],",null")!=0 )?str_replace(",","",$data[2]):null;
		$runlevelname = (isset($data[3]) && strcmp($data[3],",null")!=0 )?str_replace(",","",$data[3]):null;
		$env = $this->core->Getenvcaller();
		if( $action == "this") {
			$module = $env["MOD"];
			$function = $env["NAME"];
		} else 	if( !strpos($action,"-") && !is_null($action)){
			$module = $env["MOD"];
			$function = $action;
		} else if($action==null){
			$function=null;$module=null;
		} else {
			$module=explode("-",$action);
			$function = $module[1];
			$module = $module[0];
		}
		return $this->core->base->GetActionLink( strtolower($function),$params,$runlevelname,$module );	
	}
	# date replacing
	public function r_date($data){
		return date( (isset($data[1])?$data[1]:"Y-m-d H:i:s"));	
	}


}
?>
